package io.plus.interactive.collections;

import drds.common.$;
import io.plus.interactive.AsynchronousSocketChannelContext;
import io.plus.interactive.Configuration;
import io.plus.utils.lock.LockUtils;
import io.plus.utils.lock.ReadWriteLockMap;
import io.plus.utils.lock.ReadWriteLockSet;
import lombok.extern.slf4j.Slf4j;

import java.util.HashMap;
import java.util.HashSet;

/**
 * 一对多  (ip <--> AsynchronousSocketChannelContext)<br>
 * 一个ip有哪些客户端，该维护只在Server侧有<br>
 */
@Slf4j
public class Ips {


    /**
     * 一个IP有哪些客户端
     * key: ip
     * value: ReadWriteLockSet<AsynchronousSocketChannelContext>
     */
    private ReadWriteLockMap<String, ReadWriteLockSet<AsynchronousSocketChannelContext>> ipToChannelContextSetMap = new ReadWriteLockMap<>(new HashMap<String, ReadWriteLockSet<AsynchronousSocketChannelContext>>());
    private String rwKey = "io_ips_";

    /**
     * 和ip绑定
     */
    public void bind(AsynchronousSocketChannelContext asynchronousSocketChannelContext) {
        if (asynchronousSocketChannelContext == null) {
            return;
        }

        if (asynchronousSocketChannelContext.clientConfiguration.isShortConnection) {
            return;
        }

        try {
            String ip = asynchronousSocketChannelContext.getClientNode().getIp();
            if (AsynchronousSocketChannelContext.UNKNOWN_ADDRESS_IP.equals(ip)) {
                return;
            }

            if ($.isNullOrEmpty(ip)) {
                return;
            }

            ReadWriteLockSet<AsynchronousSocketChannelContext> channelContextSet = ipToChannelContextSetMap.get(ip);
            if (channelContextSet == null) {
                LockUtils.runWriteOrWaitRead(rwKey + ip, this, () -> {
                    if (ipToChannelContextSetMap.get(ip) == null) {
                        ipToChannelContextSetMap.put(ip, new ReadWriteLockSet<>(new HashSet<AsynchronousSocketChannelContext>()));
                    }
                });
                channelContextSet = ipToChannelContextSetMap.get(ip);
            }
            channelContextSet.add(asynchronousSocketChannelContext);
        } catch (Exception e) {
            log.error(e.toString(), e);
        }
    }

    /**
     * 一个ip有哪些客户端，有可能返回null
     */
    public ReadWriteLockSet<AsynchronousSocketChannelContext> getChannelContextSet(Configuration configuration, String ip) {
        if (configuration.isShortConnection) {
            return null;
        }

        if ($.isNullOrEmpty(ip)) {
            return null;
        }
        return ipToChannelContextSetMap.get(ip);
    }

    public ReadWriteLockMap<String, ReadWriteLockSet<AsynchronousSocketChannelContext>> getIpToChannelContextSetMap() {
        return ipToChannelContextSetMap;
    }

    /**
     * 与指定ip解除绑定
     */
    public void unbind(AsynchronousSocketChannelContext asynchronousSocketChannelContext) {
        if (asynchronousSocketChannelContext == null) {
            return;
        }

        if (asynchronousSocketChannelContext.clientConfiguration.isShortConnection) {
            return;
        }

        try {
            String ip = asynchronousSocketChannelContext.getClientNode().getIp();
            if ($.isNullOrEmpty(ip)) {
                return;
            }
            if (AsynchronousSocketChannelContext.UNKNOWN_ADDRESS_IP.equals(ip)) {
                return;
            }

            ReadWriteLockSet<AsynchronousSocketChannelContext> channelContextSet = ipToChannelContextSetMap.get(ip);
            if (channelContextSet != null) {
                channelContextSet.remove(asynchronousSocketChannelContext);
                if (channelContextSet.size() == 0) {
                    ipToChannelContextSetMap.remove(ip);
                }
            } else {
                log.info("{}, ip【{}】 找不到对应的SetWithLock", asynchronousSocketChannelContext.clientConfiguration.getName(), ip);
            }
        } catch (Exception e) {
            log.error(e.toString(), e);
        }
    }
}
